import json
import random
import os
from io import BytesIO

import openpyxl
import pandas as pd
from django.core.exceptions import ObjectDoesNotExist
from django.utils import timezone
from django.utils.encoding import escape_uri_path
from rest_framework.exceptions import ValidationError
from rest_framework.parsers import MultiPartParser, FormParser
from django.shortcuts import redirect, get_object_or_404
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.exceptions import TokenError

from django.contrib.auth import authenticate, login
from django.core.cache import caches
from django.http import FileResponse, HttpResponse, Http404
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.views import APIView
from rest_framework_simplejwt.tokens import RefreshToken

from .models import User, File, WrongQuestions
from .serializers import UserSerializer, UserListSerializer, ApiLoginSerializer, FileSerializer, WrongQuestionSerializer
from ..DepartManagement.models import Department
from ..ExamManagement.models import ExamDepart, Exam
from ..ExamManagement.serializers import ExamListSerializer
from ..PaperManagement.models import Paper, PaperQuestion
from ..PaperManagement.serializers import PaperDetailSerializer
from ..QuestionManagement.models import Questions
from ..QuestionManagement.tests import solution
from ..ResultManagement.models import Results, ResultQuestionSolution
from ..ResultManagement.serializers import ResultQuestionSolutionSerializer
from ..utils.email import EmailVerificationUtils, validater_email
from ..utils.pageNumberPagination import CustomPageNumberPagination
from ..utils.response_utils import api_response, ResponseCode
from ..utils.user_util import md5_string, MyTokenObtainPairSerializer
from apps.UserManagement.task import send_verification_email_task


class Register(APIView):
    """
    用户注册接口
    """
    permission_classes = [AllowAny]

    @swagger_auto_schema(
        operation_description="注册接口",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['username', 'password', 'name', 'email', 'phone', 'code'],
            properties={
                'username': openapi.Schema(type=openapi.TYPE_STRING, description='用户名，6-11'),
                'password': openapi.Schema(type=openapi.TYPE_STRING, description='密码，6-12'),
                'confirm_password': openapi.Schema(type=openapi.TYPE_STRING, description='确认密码'),
                'name': openapi.Schema(type=openapi.TYPE_STRING, description='姓名'),
                'email': openapi.Schema(type=openapi.TYPE_STRING, description='邮箱，'),
                'phone': openapi.Schema(type=openapi.TYPE_STRING, description='手机号，11'),
                'code': openapi.Schema(type=openapi.TYPE_STRING, description='邮件验证码'),
            }
        ),
        responses={
            200: openapi.Response(
                '注册成功！',
                openapi.Schema(
                    type=openapi.TYPE_OBJECT,
                    properties={
                        'id': openapi.Schema(type=openapi.TYPE_STRING, description='用户ID'),
                        'username': openapi.Schema(type=openapi.TYPE_STRING, description='用户名'),
                    }
                )
            ),
            400: openapi.Response('请求参数不合法'),
            409: openapi.Response('用户名被占用'),
        },
        tags=["登录注册"]
    )
    def post(self, request):
        # 1.获取请求体数据
        data = json.loads(request.body)
        username = data.get("username")
        password = data.get("password")
        confirm_password = data.get("confirm_password")
        name = data.get("name")
        user_type = 0
        status = True

        email = data.get("email")
        phone = data.get("phone")
        code = data.get("code")  # 邮件验证码

        # 邮件验证码校验
        sms_ex_key = f"sms_ex_{email}"

        redis_code = caches["sms"].get(sms_ex_key)
        if not redis_code:
            return api_response(ResponseCode.BAD_REQUEST, "验证码已过期,请重新获取")

        if str(redis_code) != code:
            return api_response(ResponseCode.BAD_REQUEST, "验证码错误,请重新输入")

        # 2.数据合法性校验
        if len(username) < 6 or len(username) > 11:
            return api_response(ResponseCode.BAD_REQUEST, "用户名不合法")

        if len(password) < 6 or len(password) > 12:
            return api_response(ResponseCode.BAD_REQUEST, "密码不合法")

        if password != confirm_password:
            return api_response(ResponseCode.BAD_REQUEST, "密码与确认密码不匹配")

        if len(phone) != 11:
            return api_response(ResponseCode.BAD_REQUEST, "手机号不合法")

        if not validater_email(email):
            return api_response(ResponseCode.BAD_REQUEST, "邮箱格式不合法")

        user = User.objects.filter(email=email).first()
        if user:
            return api_response(ResponseCode.CONFLICT, "邮箱已被占用")

        # 校验用户名是否被占用
        user_query = User.objects.filter(username=username).first()
        if user_query:
            # 被占用
            return api_response(ResponseCode.CONFLICT, "用户名被占用")

        # 存入数据表
        user = User.objects.create(username=username, password=md5_string(password), name=name,
                                   user_type=user_type, status=status, email=email, phone=phone)

        # 清除短信验证码,释放内存
        caches["sms"].delete(sms_ex_key)

        # 返回响应
        data = {
            "id": str(user.id),
            "username": user.username,
        }

        return api_response(ResponseCode.SUCCESS, "注册成功！", data)


class EmailCode(APIView):
    """
    发送邮箱验证码接口
    """
    permission_classes = [AllowAny]  # 允许任何人访问

    @swagger_auto_schema(
        operation_description="发送邮箱验证码",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['email', 'username'],
            properties={
                'email': openapi.Schema(type=openapi.TYPE_STRING, description="用户邮箱"),
                'username': openapi.Schema(type=openapi.TYPE_STRING, description='用户名'),
            }
        ),
        responses={
            200: openapi.Response('验证码发送成功'),
            400: openapi.Response('邮箱格式不正确或用户名为空'),
            429: openapi.Response('请求过于频繁'),
        },
        tags=["登录注册"]
    )
    def post(self, request):
        email = request.data.get('email')
        username = request.data.get('username')
        # 判断1分钟之内是否发过
        sms_key = f"sms_{email}"  # Redis: {"sms_1639970223@qq.com": 1016}
        r = caches["sms"].get(sms_key)
        if not username:
            return api_response(ResponseCode.BAD_REQUEST, "用户名不能为空")
        if r:
            return api_response(ResponseCode.TOO_MANY_REQUESTS, "发送过于频繁")

        if not email or '@' not in email:  # 简单的邮箱格式验证
            if not validater_email(email):
                return api_response(ResponseCode.BAD_REQUEST, "邮箱格式不正确")

        # 发送验证码
        code = EmailVerificationUtils.generate_verification_code()

        subject = '翔东渝考试系统验证码'
        message = f'尊敬的{username}，这是你的注册验证码: {code}，请在五分钟内使用。'

        # 异步发送验证码邮件
        send_verification_email_task.delay(email, subject, message)

        # 存入Redis数据库   60s:用于控制邮件发送频率
        EmailVerificationUtils.cache_verification_code(sms_key, code, 60)

        # 300s:用于邮件验证码的校验
        sms_ex_key = f"sms_ex_{email}"
        EmailVerificationUtils.cache_verification_code(sms_ex_key, code, 300)

        code_redis = caches["sms"].get(sms_ex_key)

        return api_response(ResponseCode.SUCCESS, "验证码发送成功")


class UserLogin(APIView):
    """
    用户登录接口
    """
    permission_classes = [AllowAny]  # 不需要身份认证即可登录

    @swagger_auto_schema(
        operation_description="用户登录接口",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['username_or_email', 'password'],
            properties={
                'username_or_email': openapi.Schema(
                    type=openapi.TYPE_STRING,
                    description="用户名或邮箱"
                ),
                'password': openapi.Schema(
                    type=openapi.TYPE_STRING,
                    description="用户密码"
                )
            }
        ),
        responses={
            200: openapi.Response(
                '登录成功！',
                openapi.Schema(
                    type=openapi.TYPE_OBJECT,
                    properties={
                        'userInfo': openapi.Schema(
                            type=openapi.TYPE_OBJECT,
                            properties={
                                'id': openapi.Schema(type=openapi.TYPE_STRING, description='用户ID'),
                                'username': openapi.Schema(type=openapi.TYPE_STRING, description='用户名'),
                                'name': openapi.Schema(type=openapi.TYPE_STRING, description='姓名'),
                                'user_type': openapi.Schema(type=openapi.TYPE_INTEGER,
                                                            description='用户类型 (0: 普通用户, 1: 管理员)'),
                                'status': openapi.Schema(type=openapi.TYPE_BOOLEAN,
                                                         description='账户状态 (True: 激活, False: 禁用)'),
                                'exams_number': openapi.Schema(type=openapi.TYPE_INTEGER, description='考试数量'),
                            }
                        ),
                        'token': openapi.Schema(type=openapi.TYPE_STRING, description='登录 Token')
                    }
                )
            ),
            400: '登录失败，账号或密码错误！',
            404: '用户或邮箱不存在'
        },
        tags=["登录注册"]
    )
    def post(self, request):
        username_or_email = request.data.get('username_or_email')
        password = request.data.get('password')

        if not username_or_email or not password:
            return api_response(ResponseCode.BAD_REQUEST, "账号或密码不完整，请补全")

        user = None
        if '@' in username_or_email:
            if validater_email(username_or_email):
                user = User.objects.filter(email=username_or_email).first()
            else:
                return api_response(ResponseCode.BAD_REQUEST, "邮箱格式不正确")
        else:
            user = User.objects.filter(username=username_or_email).first()
        if not user:
            return api_response(ResponseCode.NOT_FOUND, "用户或邮箱不存在")
        else:
            if md5_string(password) == user.password:
                # 密码正确，生成token
                refresh = RefreshToken.for_user(user)
                access_token = str(refresh.access_token)  # 获取访问令牌字符串
                refresh_token = str(refresh)  # 获取刷新令牌字符串

                # 用户的部门实例
                depart_profile = user.depart
                depart_user = [depart_profile, user]
                # 获取部门对应的考试
                exam_by_depart = ExamDepart.objects.filter(depart=depart_user[0])
                # 获取用户对应的考试
                exam_by_user = ExamDepart.objects.filter(user=depart_user[1])
                depart_exams = []
                user_exams = []
                if exam_by_user or exam_by_depart:
                    # 获取部门对应的考试
                    if exam_by_depart:
                        for exam_depart in exam_by_depart:
                            exam = exam_depart.exam
                            depart_exams.append(exam)

                    # 获取用户对应的考试
                    if exam_by_user:
                        for exam_user in exam_by_user:
                            exam = exam_user.exam
                            user_exams.append(exam)

                exams = depart_exams + user_exams

                data = {
                    "userInfo": {
                        "id": str(user.id),
                        "username": user.username,
                        "name": user.name,
                        "user_type": user.user_type,
                        "status": user.status,
                        "exams_number": len(exams),
                    },
                    "token": {
                        "access": access_token,
                        "refresh": refresh_token
                    }
                }
                return api_response(ResponseCode.SUCCESS, "登录成功！", data)
            else:
                return api_response(ResponseCode.BAD_REQUEST, "登录失败，账号或密码错误！")


class UserInfo(APIView):
    """
    根据id获取用户资料接口（id：string）
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]  # 需要身份认证才能访问

    @swagger_auto_schema(
        operation_description="查看个人资料接口",
        responses={
            200: openapi.Response(
                description="成功找到该用户",
                schema=openapi.Schema(
                    type=openapi.TYPE_OBJECT,
                    properties={
                        'id': openapi.Schema(type=openapi.TYPE_STRING, description='用户ID'),
                        'username': openapi.Schema(type=openapi.TYPE_STRING, description='用户名'),
                        'name': openapi.Schema(type=openapi.TYPE_STRING, description='用户名称'),
                        'user_type': openapi.Schema(type=openapi.TYPE_STRING, description='用户类型'),
                        'status': openapi.Schema(type=openapi.TYPE_STRING, description='用户状态'),
                        'phone': openapi.Schema(type=openapi.TYPE_STRING, description='用户手机号'),
                        'email': openapi.Schema(type=openapi.TYPE_STRING, description='用户邮箱'),
                        'create_time': openapi.Schema(type=openapi.TYPE_STRING, description='创建时间'),
                        'depart': openapi.Schema(type=openapi.TYPE_STRING, description='所属部门'),
                    }
                ),
            ),
            400: "未找到该用户"
        },
        tags=['用户管理']
    )
    def get(self, request, **kwargs):
        try:
            user = User.objects.get(id=kwargs['id'])
            serializer = UserSerializer(user)
            return api_response(ResponseCode.SUCCESS, "成功找到该用户", serializer.data)
        except ObjectDoesNotExist:
            return api_response(ResponseCode.NOT_FOUND, '用户不存在！')


class UserList(APIView):
    """
    查询用户列表接口
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]
    pagination_class = CustomPageNumberPagination

    @swagger_auto_schema(
        operation_description="查询所有用户信息",
        manual_parameters=[
            openapi.Parameter('keywords', openapi.IN_QUERY, description="用户名或姓名", type=openapi.TYPE_STRING),
            openapi.Parameter('depart', openapi.IN_QUERY, description="部门", type=openapi.TYPE_STRING),
            openapi.Parameter('status', openapi.IN_QUERY, description="用户状态", type=openapi.TYPE_INTEGER,
                              enum=[0, 1]),
            openapi.Parameter('user_type', openapi.IN_QUERY, description="用户类型", type=openapi.TYPE_INTEGER,
                              enum=[0, 1]),
        ],
        responses={200: UserListSerializer(many=True),
                   400: "用户列表为空"},  # 返回所有用户的列表
        tags=['用户管理']
    )
    def get(self, request):
        try:
            keywords = request.query_params.get("keywords", "")
            status = request.query_params.get("status", None)
            page = request.query_params.get("pageNo", 1)
            page_size = request.query_params.get("pageSize", 10)
            user_type = request.query_params.get("user_type")
            depart_id = request.query_params.get("depart")

            # 根据传入的参数进行查询
            if status and keywords:
                # 根据 status 和 keywords 组合查询
                users = User.objects.filter_by_status_and_keywords(status=status, keywords=keywords)
            elif status:
                # 单独根据 status 查询
                users = User.objects.filter_by_status(status=status)
            elif keywords:
                # 单独根据 keywords 查询
                users = User.objects.filter_by_keywords(keywords)
            else:
                # 没有提供查询条件，返回所有用户
                users = User.objects.all()
            if depart_id:
                users = users.filter(depart=depart_id)

            if not users:
                return api_response(ResponseCode.SUCCESS, "没有找到符合条件的用户", [])
            if user_type:
                users = users.filter(user_type=user_type)
            paginator = CustomPageNumberPagination()
            # 设置分页参数
            paginator.page_size = int(page_size)  # 每页的条数
            paginator.page_query_param = 'pageNo'  # 页码的参数名
            paginator.page_size_query_param = 'page_size'  # 每页数据条数的参数名
            result_page = paginator.paginate_queryset(users, request)
            serializer = UserListSerializer(result_page, many=True)  # 序列化多个用户
            paginated_response = paginator.get_paginated_response(serializer.data)

            return api_response(ResponseCode.SUCCESS, "查询成功", paginated_response.data)

        except ValidationError as e:
            # 捕获参数验证错误
            return api_response(ResponseCode.BAD_REQUEST, f"请求参数错误: {str(e)}")
        except Exception as e:
            # 捕获所有其他异常
            return api_response(ResponseCode.INTERNAL_SERVER_ERROR, f"服务器内部错误: {str(e)}")


class GetUsers(APIView):
    """
    查询用户列表接口（不分页）
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    @swagger_auto_schema(
        operation_description="获取用户列表",
        responses={200: UserListSerializer(many=True),
                   400: "用户列表为空"},  # 返回所有用户的列表
        tags=['用户管理']
    )
    def get(self, request):
        try:
            users = User.objects.all()
            if not users:
                return api_response(ResponseCode.SUCCESS, "没有找到符合条件的用户", [])
            serializer = UserListSerializer(users, many=True)  # 序列化多个用户

            return api_response(ResponseCode.SUCCESS, "查询成功", serializer.data)

        except Exception as e:
            # 捕获所有其他异常
            return api_response(ResponseCode.INTERNAL_SERVER_ERROR, f"服务器内部错误: {str(e)}")


class LogoutView(APIView):
    """
    用户登出接口
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]  # 需要身份认证才能登出

    @swagger_auto_schema(
        operation_description="用户登出接口",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['refresh'],
            properties={
                'refresh': openapi.Schema(
                    type=openapi.TYPE_STRING,
                    description="refresh token"
                ),
            }
        ),
        responses={
            200: '登出成功！',
            400: '用户未登录或token无效！'
        },
        tags=["登录注册"]
    )
    def delete(self, request):
        try:
            # 获取用户的 Refresh Token
            refresh_token = request.data.get('refresh')
            if not refresh_token:
                return api_response(ResponseCode.BAD_REQUEST, "未传入token")

            # 验证 refresh token 并将其加入黑名单
            refresh = RefreshToken(refresh_token)
            refresh.blacklist()  # 将 token 加入黑名单
            return api_response(ResponseCode.SUCCESS, "登出成功！")
        except TokenError:
            return api_response(ResponseCode.BAD_REQUEST, "无效的token，登出失败！")


class ApiLogin(APIView):
    """接口文档的登录接口"""

    serializer_class = ApiLoginSerializer
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    def post(self, request):
        username = request.data.get("username")
        password = request.data.get("password")
        user_obj = authenticate(
            request,
            username=username,
            password=md5_string(password),
        )
        if user_obj:
            login(request, user_obj)
            return redirect("/")
        else:
            return api_response(ResponseCode.BAD_REQUEST, "账号/密码错误")


class EmailFindPwd(APIView):
    """
    找回密码发送邮箱验证码接口
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]  # 允许任何人访问

    @swagger_auto_schema(
        operation_description="找回密码发送邮箱验证码",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['email', 'username'],
            properties={
                'email': openapi.Schema(type=openapi.TYPE_STRING, description="用户邮箱"),
                'username': openapi.Schema(type=openapi.TYPE_STRING, description='用户名'),
            }
        ),
        responses={200: '验证码发送成功', 400: '邮箱格式不正确'},
        tags=['用户管理']
    )
    def post(self, request):
        email = request.data.get('email')
        username = request.data.get('username')
        # 判断1分钟之内是否发过
        sms_key = f"find_{email}"
        r = caches["sms"].get(sms_key)
        if r:
            return api_response(ResponseCode.BAD_REQUEST, "发送过于频繁")

        if not email or '@' not in email:
            return api_response(ResponseCode.BAD_REQUEST, "邮箱格式不正确")

        # 发送验证码
        code = EmailVerificationUtils.generate_verification_code()

        subject = '翔东渝考试系统验证码'
        message = f'尊敬的{username}，这是你的找回密码验证码: {code}，请在五分钟内使用。'

        # 异步发送验证码邮件
        send_verification_email_task.delay(email, subject, message)

        # 存入Redis数据库   60s:用于控制邮件发送频率
        EmailVerificationUtils.cache_verification_code(sms_key, code, 60)

        # 300s:用于邮件验证码的校验
        sms_ex_key = f"find_ex_{email}"
        EmailVerificationUtils.cache_verification_code(sms_ex_key, code, 300)

        user = User.objects.filter(username=username).first()
        if user.email != email:
            return api_response(ResponseCode.BAD_REQUEST, "用户名与邮箱不匹配")

        return api_response(ResponseCode.SUCCESS, "验证码发送成功")


class FindPwd(APIView):
    """
    用户找回密码接口
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]  # 允许任何人访问

    @swagger_auto_schema(
        operation_description="找回密码",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['email', 'username', 'code', 'password', 'repassword'],
            properties={
                'email': openapi.Schema(type=openapi.TYPE_STRING, description="用户邮箱"),
                'username': openapi.Schema(type=openapi.TYPE_STRING, description='用户名'),
                'code': openapi.Schema(type=openapi.TYPE_STRING, description='验证码'),
                'password': openapi.Schema(type=openapi.TYPE_STRING, description='新密码'),
                'repassword': openapi.Schema(type=openapi.TYPE_STRING, description='确认密码'),
            }
        ),
        responses={200: '验证码发送成功', 400: '邮箱格式不正确'},
        tags=['用户管理']
    )
    def post(self, request):
        code = request.data.get('code')
        username = request.data.get('username')
        email = request.data.get('email')
        password = request.data.get('password')
        repassword = request.data.get('repassword')

        # 邮件验证码校验
        sms_ex_key = f"find_ex_{email}"
        redis_code = caches["sms"].get(sms_ex_key)

        if not redis_code:
            return api_response(ResponseCode.BAD_REQUEST, "验证码已过期")

        if str(redis_code) != code:
            return api_response(ResponseCode.BAD_REQUEST, "验证码错误")

        if password != repassword:
            return api_response(ResponseCode.BAD_REQUEST, "密码不一致")

        if len(password) < 6 or len(password) > 12:
            return api_response(ResponseCode.BAD_REQUEST, "密码长度在6-12位之间")

        user = User.objects.filter(username=username).first()
        if user is None:
            return api_response(ResponseCode.BAD_REQUEST, "用户不存在")
        if user.email != email:
            return api_response(ResponseCode.BAD_REQUEST, "用户名与邮箱不匹配")

        user.password = md5_string(password)
        user.save()

        caches["sms"].delete(sms_ex_key)

        return api_response(ResponseCode.SUCCESS, "密码修改成功")


class AddUsers(APIView):
    """
    批量添加用户
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]
    parser_classes = (MultiPartParser, FormParser)  # 指定使用 MultiPartParser 和 FormParser 解析器

    @swagger_auto_schema(
        operation_description="批量添加用户",
        manual_parameters=[
            openapi.Parameter(
                name='file',
                in_=openapi.IN_FORM,
                type=openapi.TYPE_FILE,
                description='Excel文件路径',
                required=True,
            ),
        ],
        responses={200: '添加成功', 400: '添加失败'},
        tags=['用户管理']
    )
    def post(self, request):
        file = request.FILES.get('file')
        if not file:
            return api_response(ResponseCode.BAD_REQUEST, "未上传文件")

        try:
            df = pd.read_excel(file)
            depart_map = []
            for index, row in df.iterrows():
                name = str(row['姓名']).strip()
                phone = str(row['联系电话']).strip()
                email = str(row['邮箱']).strip()
                depart = str(row['部门']).strip()
                if pd.isnull(depart) or depart == 'nan' or depart == '':
                    depart = "其他"
                depart_map.append(depart)

                # 去重
                depart_map = list(set(depart_map))

                while True:
                    username = str(random.randint(100000, 999999))
                    user_query = User.objects.filter(username=username).first()
                    if not user_query:
                        break

                # 校验数据合法性
                if not email or '@' not in email:
                    return api_response(ResponseCode.BAD_REQUEST, f"邮箱格式不正确: {email}")

                if len(phone) != 11:
                    return api_response(ResponseCode.BAD_REQUEST, f"手机号不合法: {phone}")

                # 校验邮箱是否被占用
                user_query = User.objects.filter(email=email)
                if user_query.exists():
                    return api_response(ResponseCode.BAD_REQUEST, f"邮箱已被占用: {email}")

                depart_profile = Department.objects.filter(name=depart).first()
                if not depart_profile:
                    depart_profile = Department.objects.create(name=depart)

                user = User.objects.create(name=name, email=email, phone=phone, username=username,
                                           password=md5_string('a123456'), depart=depart_profile)

            return api_response(ResponseCode.SUCCESS, "用户添加成功")
        except Exception as e:
            return api_response(ResponseCode.BAD_REQUEST, f"添加失败: {str(e)}")


class EditorAdmin(APIView):
    """
    管理员修改用户资料接口
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]  # 需要身份认证才能访问

    @swagger_auto_schema(
        operation_description="管理员修改用户信息",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['username', 'name', 'phone', 'email', 'user_type', 'status', 'id', 'depart'],
            properties={
                'username': openapi.Schema(type=openapi.TYPE_STRING, description="用户名"),
                'name': openapi.Schema(type=openapi.TYPE_STRING, description="用户姓名"),
                'phone': openapi.Schema(type=openapi.TYPE_STRING, description="phone"),
                'email': openapi.Schema(type=openapi.TYPE_STRING, description="email"),
                'user_type': openapi.Schema(type=openapi.TYPE_STRING, description="用户类型"),
                'status': openapi.Schema(type=openapi.TYPE_STRING, description="是否激活"),
                'id': openapi.Schema(type=openapi.TYPE_STRING, description="用户id"),
                'depart': openapi.Schema(type=openapi.TYPE_STRING, description="部门id"),
            }
        ),
        responses={200: '修改成功', 400: '修改失败'},
        tags=['用户管理']
    )
    def put(self, request):
        try:
            user_id = request.data.get('id')
            user = User.objects.filter(id=user_id).first()
            if not user:
                return api_response(ResponseCode.BAD_REQUEST, "用户不存在")

            username = request.data.get('username')
            name = request.data.get('name')
            phone = request.data.get('phone')
            email = request.data.get('email')
            user_type = request.data.get('user_type')
            status = request.data.get('status')
            depart_id = request.data.get('depart')

            depart = Department.objects.get(id=depart_id)
            if not depart:
                return api_response(ResponseCode.BAD_REQUEST, "部门不存在")

            if user.username != username:
                user_query = User.objects.filter(username=username).first()
                if user_query:
                    return api_response(ResponseCode.CONFLICT, "用户名被占用")

            if len(phone) != 11:
                return api_response(ResponseCode.BAD_REQUEST, "手机号不合法")

            if not validater_email(email):
                return api_response(ResponseCode.BAD_REQUEST, "邮箱格式不合法")

            # 管理员修改用户的邮箱
            if user.email != email:
                user_email = User.objects.filter(email=email).first()
                if user_email:
                    return api_response(ResponseCode.CONFLICT, "邮箱已被占用")

            user.username = username
            user.name = name
            user.phone = phone
            user.email = email
            user.user_type = user_type
            user.status = status
            user.depart = depart
            user.save()

            return api_response(ResponseCode.SUCCESS, "修改成功")
        except ValidationError as e:
            return api_response(ResponseCode.BAD_REQUEST, f"请求参数错误: {str(e)}")
        except Exception as e:
            return api_response(ResponseCode.INTERNAL_SERVER_ERROR, f"服务器内部错误: {str(e)}")


class DeleteUsers(APIView):
    """
    批量删除用户
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]  # 需要身份认证才能访问

    @swagger_auto_schema(
        operation_description="批量删除用户",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['ids'],
            properties={
                'ids': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_STRING),
                                      description="用户ID列表"),
            }
        ),
        responses={
            200: openapi.Response('删除成功'),
            400: openapi.Response('请求参数不合法'),
            404: openapi.Response('部分用户不存在'),
        },
        tags=['用户管理']
    )
    def delete(self, request):
        ids = request.data.get('ids', [])
        if not ids:
            return api_response(ResponseCode.BAD_REQUEST, "用户ID列表不能为空")

        for id in ids:
            user = User.objects.filter(id=id).first()
            if not user:
                return api_response(ResponseCode.NOT_FOUND, f"用户ID不存在: {id}")
            user.delete()

        return api_response(ResponseCode.SUCCESS, "删除成功")


class EditorUser(APIView):
    """
    用户修改自己资料接口
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    @swagger_auto_schema(
        operation_description="用户修改自己资料接口",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['username', 'name', 'phone', 'password'],
            properties={
                'username': openapi.Schema(type=openapi.TYPE_STRING, description="username"),
                'name': openapi.Schema(type=openapi.TYPE_STRING, description="name"),
                'phone': openapi.Schema(type=openapi.TYPE_STRING, description="phone"),
                'id': openapi.Schema(type=openapi.TYPE_STRING, description="id"),
            }
        ),
        responses={200: '修改成功', 400: '修改失败'},
        tags=['用户管理']
    )
    def put(self, request):
        user_id = request.data.get('id')
        user = User.objects.filter(id=user_id).first()
        if not user:
            return api_response(ResponseCode.BAD_REQUEST, "用户不存在")

        username = request.data.get('username')
        name = request.data.get('name')
        phone = request.data.get('phone')

        if user.username != username:
            user_query = User.objects.filter(username=username).first()
            if user_query:
                return api_response(ResponseCode.CONFLICT, "用户名被占用")

        if len(phone) != 11:
            return api_response(ResponseCode.BAD_REQUEST, "手机号不合法")

        user.username = username
        user.name = name
        user.phone = phone
        user.save()

        return api_response(ResponseCode.SUCCESS, "修改成功")


class ChangeEmailCode(APIView):
    """
    修改邮箱发送邮箱验证码接口
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]  # 允许任何人访问

    @swagger_auto_schema(
        operation_description="修改邮箱发送邮箱验证码",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['email'],
            properties={
                'email': openapi.Schema(type=openapi.TYPE_STRING, description="用户邮箱"),
            }
        ),
        responses={200: '验证码发送成功', 400: '邮箱格式不正确'},
        tags=['用户管理']
    )
    def post(self, request):
        email = request.data.get('email')
        # 判断1分钟之内是否发过
        change_email_key = f"change_email_{email}"
        r = caches["sms"].get(change_email_key)
        if r:
            return api_response(ResponseCode.BAD_REQUEST, "发送过于频繁")

        if not email or '@' not in email:
            return api_response(ResponseCode.BAD_REQUEST, "邮箱格式不正确")

        # 发送验证码
        code = EmailVerificationUtils.generate_verification_code()

        subject = '翔东渝考试系统验证码'
        message = f'你好！{email}，该邮箱正在使用解绑功能，这是你的验证码: {code}，请在五分钟内使用。'

        # 异步发送验证码邮件
        send_verification_email_task.delay(email, subject, message)

        # 存入Redis数据库   60s:用于控制邮件发送频率
        EmailVerificationUtils.cache_verification_code(change_email_key, code, 60)

        # 300s:用于邮件验证码的校验
        change_email_key = f"change_email_ex_{email}"
        EmailVerificationUtils.cache_verification_code(change_email_key, code, 300)

        if not User.objects.filter(email=email).exists():
            return api_response(ResponseCode.NOT_FOUND, "邮箱不存在")

        return api_response(ResponseCode.SUCCESS, "验证码发送成功")


class ChangeEmail(APIView):
    """
    修改邮箱接口
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    @swagger_auto_schema(
        operation_description="用户修改邮箱接口",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['new_email', 'id'],
            properties={
                'new_email': openapi.Schema(type=openapi.TYPE_STRING, description="新邮箱"),
                'id': openapi.Schema(type=openapi.TYPE_STRING, description="id"),
                'code': openapi.Schema(type=openapi.TYPE_STRING, description="验证码"),
            }
        ),
        responses={200: '修改成功', 400: '修改失败，邮箱格式不合法', 404: '用户不存在', 409: '邮箱已存在',
                   500: '服务器内部错误'},
        tags=['用户管理']
    )
    def post(self, request):
        new_email = request.data.get("new_email", None)
        user_id = request.data.get("id")
        code = request.data.get("code")

        if not new_email:
            return api_response(ResponseCode.BAD_REQUEST, "新邮箱不能为空")
        if not validater_email(new_email):
            return api_response(ResponseCode.BAD_REQUEST, "邮箱格式不正确")
        if User.objects.filter(email=new_email).exists():
            return api_response(ResponseCode.CONFLICT, "该邮箱已被绑定，请输入其他邮箱")
        try:
            user = User.objects.filter(id=user_id).first()
            if not user:
                return api_response(ResponseCode.NOT_FOUND, "用户不存在")
            # 邮件验证码校验
            change_email_ex_key = f"change_email_ex_{user.email}"
            redis_code = caches["sms"].get(change_email_ex_key)

            if not redis_code:
                return api_response(ResponseCode.BAD_REQUEST, "验证码已过期")

            if str(redis_code) != code:
                return api_response(ResponseCode.BAD_REQUEST, "验证码错误")
            user.email = new_email
            user.save()
            return api_response(ResponseCode.SUCCESS, "邮箱修改成功")
        except Exception as e:
            return api_response(ResponseCode.INTERNAL_SERVER_ERROR, f"服务器错误: {str(e)}")


class PassUser(APIView):
    """
    用户修改密码接口
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    @swagger_auto_schema(
        operation_description="用户修改自己密码接口",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['password', 'new_password', 'confirm_password', 'id'],
            properties={
                'password': openapi.Schema(type=openapi.TYPE_STRING, description="password"),
                'new_password': openapi.Schema(type=openapi.TYPE_STRING, description="new_password"),
                'confirm_password': openapi.Schema(type=openapi.TYPE_STRING, description="confirm_password"),
                'id': openapi.Schema(type=openapi.TYPE_STRING, description="id"),
            }
        ),
        responses={200: '修改成功', 400: '修改失败'},
        tags=['用户管理']
    )
    def put(self, request):
        user_id = request.data.get('id')
        user = User.objects.filter(id=user_id).first()
        if not user:
            return api_response(ResponseCode.BAD_REQUEST, "用户不存在")

        password = request.data.get('password')
        new_password = request.data.get('new_password')
        confirm_password = request.data.get('confirm_password')

        if md5_string(password) != user.password:
            return api_response(ResponseCode.BAD_REQUEST, "密码错误")

        if len(new_password) < 6 or len(new_password) > 12:
            return api_response(ResponseCode.BAD_REQUEST, "密码长度不合法")

        if new_password != confirm_password:
            return api_response(ResponseCode.BAD_REQUEST, "两次密码不一致")

        user.password = md5_string(new_password)
        user.save()

        return api_response(ResponseCode.SUCCESS, "修改成功")


class AddUser(APIView):
    """
            添加单个用户
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    @swagger_auto_schema(
        operation_description="添加单个用户",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['username', 'name', 'user_type', 'status', 'phone', 'email'],
            properties={
                'username': openapi.Schema(type=openapi.TYPE_STRING, description="username"),
                'name': openapi.Schema(type=openapi.TYPE_STRING, description="name"),
                'user_type': openapi.Schema(type=openapi.TYPE_STRING, description="user_type"),
                'phone': openapi.Schema(type=openapi.TYPE_STRING, description="phone"),
                'email': openapi.Schema(type=openapi.TYPE_STRING, description="email"),
                'password': openapi.Schema(type=openapi.TYPE_STRING, description="password"),
                'depart': openapi.Schema(type=openapi.TYPE_STRING, description="depart"),
            }
        ),
        responses={200: '添加成功', 400: '添加失败'},
        tags=['用户管理']
    )
    def post(self, request):
        try:
            username = request.data.get('username')
            name = request.data.get('name')
            user_type = request.data.get('user_type')
            phone = request.data.get('phone')
            email = request.data.get('email')
            password = request.data.get('password')
            depart_id = request.data.get('depart')

            depart = Department.objects.get(id=depart_id)
            if password:
                password = md5_string(password)
            else:
                password = "a123456"

            user_username = User.objects.filter(username=username).first()
            if user_username:
                return api_response(ResponseCode.CONFLICT, "用户名被占用")

            user_email = User.objects.filter(email=email).first()
            if user_email:
                return api_response(ResponseCode.CONFLICT, "邮箱被占用")

            User.objects.create(username=username,
                                name=name,
                                user_type=user_type,
                                phone=phone,
                                email=email,
                                password=password,
                                depart=depart)

            return api_response(ResponseCode.SUCCESS, "添加成功")
        except ValidationError as e:
            return api_response(ResponseCode.BAD_REQUEST, f"请求参数错误: {str(e)}")

        except Exception as e:
            return api_response(ResponseCode.INTERNAL_SERVER_ERROR, f"服务器内部错误: {str(e)}")


class ResetPassword(APIView):
    """
    批量重置用户密码
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    @swagger_auto_schema(
        operation_description="批量重置用户密码",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['ids'],
            properties={
                'ids': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_STRING),
                                      description="用户ID列表"),
            }
        ),
        responses={
            200: openapi.Response('重置成功'),
            400: openapi.Response('请求参数不合法'),
            404: openapi.Response('部分用户不存在'),
        },
        tags=['用户管理']
    )
    def put(self, request):
        ids = request.data.get('ids', [])
        if not ids:
            return api_response(ResponseCode.BAD_REQUEST, "用户ID列表不能为空")

        for id in ids:
            user = User.objects.filter(id=id).first()
            if not user:
                return api_response(ResponseCode.NOT_FOUND, f"用户ID不存在: {id}")
            user.password = md5_string('a123456')
            user.save()

        return api_response(ResponseCode.SUCCESS, "重置成功")


class ChangeStatus(APIView):
    """
                切换用户状态
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    @swagger_auto_schema(
        operation_description="切换用户状态",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['id'],
            properties={
                'id': openapi.Schema(type=openapi.TYPE_STRING, description="id"), }
        ),
        responses={200: '切换成功', 400: '切换失败'},
        tags=['用户管理']
    )
    def put(self, request):
        user_id = request.data.get('id')
        user = User.objects.filter(id=user_id).first()
        if not user:
            return api_response(ResponseCode.BAD_REQUEST, "用户不存在")

        if user.status:
            user.status = False
            user.save()
        else:
            user.status = True
            user.save()

        return api_response(ResponseCode.SUCCESS, "切换成功")


class FileUploadView(APIView):
    """
    上传用户信息模板
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]
    parser_classes = [MultiPartParser, FormParser]  # 支持多部分表单数据

    @swagger_auto_schema(
        operation_description="上传模板",
        manual_parameters=[
            openapi.Parameter(
                name='file',
                in_=openapi.IN_FORM,
                type=openapi.TYPE_FILE,
                description='Excel文件路径',
                required=True,
            ),
        ],
        responses={200: '添加成功', 400: '添加失败'},
        tags=['用户管理']
    )
    def post(self, request, *args, **kwargs):
        # 使用序列化器验证上传的文件
        serializer = FileSerializer(data=request.data)
        if serializer.is_valid():
            # 保存文件并返回成功响应
            serializer.save()
            return api_response(ResponseCode.SUCCESS, "添加成功")
        return api_response(ResponseCode.BAD_REQUEST, "添加失败")


class FileDownloadView(APIView):
    """
    下载用户信息模板
    """

    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    @swagger_auto_schema(
        operation_description="根据文件 ID 下载文件",
        responses={
            200: openapi.Response(
                description="文件下载成功",
                examples={
                    "application/pdf": {
                        "file": "http://example.com/file.pdf"
                    }
                }
            ),
            404: "文件未找到",
        },
        manual_parameters=[
            openapi.Parameter(
                "file_id", openapi.IN_PATH, description="文件的 ID", type=openapi.TYPE_STRING
            ),
        ]
    )
    def get(self, request, file_id, *args, **kwargs):
        # 查找文件对象
        file_instance = get_object_or_404(File, id=file_id)

        # 获取文件的路径（包括文件名）
        file_path = file_instance.file.path  # 使用 FileField 的 `path` 获取文件的绝对路径

        # 获取文件扩展名
        file_extension = os.path.splitext(file_path)[-1].lower().strip('.')

        # 设置文件类型映射
        content_types = {
            'pdf': 'application/pdf',
            'zip': 'application/zip',
            'doc': 'application/msword',
            'xls': 'application/vnd.ms-excel',
            'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'ppt': 'application/vnd.ms-powerpoint',
            'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
            'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'txt': 'text/plain',
            'csv': 'text/csv',
            'jpg': 'image/jpeg',
            'png': 'image/png',
            'gif': 'image/gif',
            'bmp': 'image/bmp',
            'svg': 'image/svg+xml',
            'html': 'text/html',
        }

        # 获取文件的 Content-Type，如果没有匹配的类型，默认为 application/octet-stream
        content_type = content_types.get(file_extension, 'application/octet-stream')

        # 获取文件名
        file_name = file_instance.file.name.split('/')[-1]  # 从文件路径中提取文件名

        try:
            if file_extension == 'xlsx':
                # 处理 Excel 文件的逻辑
                wb = openpyxl.load_workbook(file_path)
                sheet = wb.active

                # 遍历 Excel 内容，确保每一列数据的类型正确
                for row in sheet.iter_rows():
                    for cell in row:
                        if isinstance(cell.value, str):  # 如果是字符串
                            cell.value = cell.value.strip()  # 去掉多余的空格
                        elif isinstance(cell.value, float):  # 如果是数字
                            cell.value = round(cell.value, 2)  # 设置为两位小数

                # 保存到内存
                file_stream = BytesIO()
                wb.save(file_stream)
                file_stream.seek(0)

                # 返回文件响应，将文件的二进制数据作为内容返回
                response = HttpResponse(file_stream, content_type=content_type)

            else:
                # 对其他文件类型的处理逻辑（如 PDF、CSV、图片等）
                with open(file_path, 'rb') as file:
                    response = HttpResponse(file.read(), content_type=content_type)

        except FileNotFoundError:
            return api_response(ResponseCode.NOT_FOUND, "文件未找到")

        # 设置 Content-Disposition 头部来指定下载的文件名
        response['Content-Disposition'] = f"attachment; filename*=UTF-8''{escape_uri_path(file_name)}"

        # 返回下载文件信息的 JSON 响应，附带文件 URL 和文件名
        download_info = {
            "file_url": request.build_absolute_uri(file_instance.file.url),  # 直接返回文件的绝对 URL
            "file_name": file_name,
            "file_size": os.path.getsize(file_path),  # 返回文件的大小（字节）
            "content_type": content_type,
        }

        return api_response(ResponseCode.SUCCESS, "下载成功", download_info)


class GetExams(APIView):
    """
        获取需要参加的考试列表
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]
    pagination_class = CustomPageNumberPagination

    @swagger_auto_schema(
        operation_description="用户查看自己需要参加的考试列表",
        manual_parameters=[
            openapi.Parameter('user_id', openapi.IN_QUERY, description="用户ID", type=openapi.TYPE_STRING),
        ],
        responses={
            200: ExamListSerializer(many=True),
            400: "请求参数错误",
            500: "服务器内部错误"
        },
        tags=['用户管理']
    )
    def get(self, request):
        try:
            user_id = request.query_params.get('user_id')
            # 用户实例
            user_profile = User.objects.filter(id=user_id).first()

            if not user_profile:
                return api_response(ResponseCode.BAD_REQUEST, '用户不存在')

            # 用户的部门实例
            depart_profile = user_profile.depart
            depart_user = [depart_profile, user_profile]
            # 获取部门对应的考试
            exam_by_depart = ExamDepart.objects.filter(depart=depart_user[0])
            # 获取用户对应的考试
            exam_by_user = ExamDepart.objects.filter(user=depart_user[1])

            if not exam_by_user and not exam_by_depart:
                return api_response(ResponseCode.SUCCESS, '用户未参加考试', [])

            # 获取部门对应的考试
            depart_exams = []
            if exam_by_depart:
                for exam_depart in exam_by_depart:
                    exam = exam_depart.exam
                    depart_exams.append(exam)

            # 获取用户对应的考试
            user_exams = []
            if exam_by_user:
                for exam_user in exam_by_user:
                    exam = exam_user.exam
                    user_exams.append(exam)

            exams = depart_exams + user_exams

            if not exams:
                return api_response(ResponseCode.SUCCESS, "未找到符合条件的考试", [])

            exam_ids = [e.id for e in exams]

            # 遍历考试并删除子考试
            for exam in exams[:]:  # 使用切片避免在遍历时修改列表
                if exam.parent and exam.parent.id in exam_ids:  # 判断父考试是否在 exams 中
                    exams.remove(exam)  # 删除当前考试

            serializer = ExamListSerializer(exams, many=True)

            return api_response(ResponseCode.SUCCESS, '考试列表获取成功', serializer.data)
        except ValidationError as e:
            return api_response(ResponseCode.BAD_REQUEST, f"请求参数错误: {str(e)}")
        except Exception as e:
            return api_response(ResponseCode.INTERNAL_SERVER_ERROR, f"服务器内部错误: {str(e)}")


class StartAnswering(APIView):
    """
    开始考试，判断用户是否能考试
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    @swagger_auto_schema(
        operation_description="开始考试，判断用户是否能考试",
        manual_parameters=[
            openapi.Parameter('user_id', openapi.IN_QUERY, description="用户ID", type=openapi.TYPE_STRING),
            openapi.Parameter('exam_id', openapi.IN_QUERY, description="考试ID", type=openapi.TYPE_STRING),
        ],
        responses={
            200: ExamListSerializer(many=True),
            400: "请求参数错误",
            500: "服务器内部错误"
        },
        tags=['用户管理']
    )
    def get(self, request):
        try:
            user_id = request.query_params.get('user_id')
            exam_id = request.query_params.get('exam_id')
            try:
                user = User.objects.get(id=user_id)
            except ObjectDoesNotExist:
                return api_response(ResponseCode.BAD_REQUEST, '用户不存在')
            try:
                exam = Exam.objects.get(id=exam_id)
            except ObjectDoesNotExist:
                return api_response(ResponseCode.BAD_REQUEST, '考试不存在')

            current_tz = timezone.get_current_timezone()
            now = timezone.now()

            # 将考试结束时间转换为当前时区的 aware datetime
            end_time = exam.end_time.astimezone(current_tz)

            # 判断考试是否已结束
            if end_time < now:
                time_left = '00:00:00'
            else:
                # 计算剩余时间
                time_left = end_time - now
                total_seconds_left = time_left.days * 24 * 3600 + time_left.seconds  # 总秒数

                hours_left = total_seconds_left // 3600  # 小时数
                remaining_seconds = total_seconds_left % 3600
                minutes_left = remaining_seconds // 60  # 分钟数
                seconds_left = remaining_seconds % 60  # 秒数

                # 获取当前时间并计算剩余时间
                time_left = f"{hours_left:02d}:{minutes_left:02d}:{seconds_left:02d}"

            # Redis缓存用户开考信息
            cache_key = f"user_exam_{user_id}_{exam_id}"

            # 从Redis缓存中获取数据
            cached_result = caches["sms"].get(cache_key)
            if cached_result and not exam.re_answer:
                return api_response(ResponseCode.BAD_REQUEST, '只能参加一次考试')

            if exam.status == 0:
                return api_response(ResponseCode.BAD_REQUEST, '考试未开始')
            elif exam.status == 2:
                return api_response(ResponseCode.BAD_REQUEST, '考试已结束')
            elif exam.status == 3:
                return api_response(ResponseCode.BAD_REQUEST, '考试异常，请联系管理员')
            elif exam.status == 1:
                # 判断用户是否已经参加考试
                result = Results.objects.filter(exam=exam, user=user).first()
                if result:
                    return api_response(ResponseCode.BAD_REQUEST, '你已提交过一次答卷')
                else:
                    paper = exam.paper
                    if not paper:
                        api_response(ResponseCode.BAD_REQUEST, '本场考试没有试卷，请联系管理员')
                    serializer = PaperDetailSerializer(paper)

                    # 将剩余时间添加到序列化数据
                    data = serializer.data
                    data['countdown'] = time_left
                    # 设置Redis缓存，缓存到考试结束时间
                    caches["sms"].set(cache_key, "exam_started", timeout=(end_time - now).total_seconds())

                    return api_response(ResponseCode.SUCCESS, '试卷获取成功，可以开始答题', data)
        except ValidationError as e:
            return api_response(ResponseCode.BAD_REQUEST, f"请求参数错误: {str(e)}")
        except Exception as e:
            return api_response(ResponseCode.INTERNAL_SERVER_ERROR, f"服务器内部错误: {str(e)}")


class CreateResult(APIView):
    """
    提交答题卡
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    @swagger_auto_schema(
        operation_description="提交答题卡",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['exam_id', 'user_id', 'questions_solutions'],
            properties={
                'exam_id': openapi.Schema(type=openapi.TYPE_STRING, description='考试ID'),
                'user_id': openapi.Schema(type=openapi.TYPE_STRING, description='用户ID'),
                'questions_solutions': openapi.Schema(
                    type=openapi.TYPE_ARRAY,
                    items=openapi.Schema(
                        type=openapi.TYPE_OBJECT,
                        properties={
                            'question': openapi.Schema(type=openapi.TYPE_STRING, description='试题ID'),
                            'solution': openapi.Schema(type=openapi.TYPE_STRING, description='答题内容'),
                        },
                    ),
                    description='试题数组，每个试题包含试题ID和答题内容',
                )
            },
        ),
        responses={
            200: openapi.Response(
                '创建成功！',
                openapi.Schema(
                    type=openapi.TYPE_OBJECT,
                    properties={
                        'id': openapi.Schema(type=openapi.TYPE_STRING, description='答题卡id'),
                    }
                )
            ),
            400: openapi.Response('请求参数不合法'),
            409: openapi.Response('答题卡已存在'),
            404: openapi.Response('答题卡不存在'),
        },
        tags=["用户管理"]
    )
    def post(self, request):
        try:
            exam_id = request.data.get('exam_id')
            user_id = request.data.get('user_id')
            questions_solutions = request.data.get('questions_solutions', [])

            exam = Exam.objects.filter(id=exam_id).first()
            if not exam:
                return api_response(ResponseCode.NOT_FOUND, '考试不存在')

            user = User.objects.filter(id=user_id).first()
            if not user:
                return api_response(ResponseCode.NOT_FOUND, '用户不存在')

            result_profile = Results.objects.filter(exam=exam, user=user).first()

            if exam.re_answer == 0 and result_profile:
                return api_response(ResponseCode.CONFLICT, '不能重复作答')

            if exam.start_time > timezone.now():
                return api_response(ResponseCode.BAD_REQUEST, '考试未开始')
            if exam.end_time < timezone.now():
                return api_response(ResponseCode.BAD_REQUEST, '考试已结束')

            total_score = 0
            question_list = []
            solution_list = []
            score_list = []
            for question_info in questions_solutions:
                question_id = question_info.get('question')
                solution = question_info.get('solution')
                solution_list.append(solution)

                # 找到试卷试题关联表 拿到分数
                question = Questions.objects.filter(id=question_id).first()
                question_list.append(question)
                paper_profile = Paper.objects.filter(id=exam.paper.id).first()
                paper_question = PaperQuestion.objects.filter(question=question, paper=paper_profile).first()

                if question.type in ["single", "judge"]:
                    if solution == question.answer:
                        score = paper_question.score
                        total_score += score
                        score_list.append(score)
                    else:
                        score_list.append(0)

                elif question.type == "multiple":  # "0;2"
                    answer_index = list(question.get_answer().split(";"))
                    solution = list(solution.split(";"))

                    if set(solution).issubset(set(answer_index)):
                        score = int((len(solution) / len(answer_index)) * paper_question.score)
                        total_score += score
                        score_list.append(score)
                    else:
                        score_list.append(0)

                elif question.type == "short":
                    keywords = list(question.get_keywords())
                    solution = list(solution.split(";"))
                    count_in_solution = sum(1 for a in keywords if a in solution)
                    score = int((count_in_solution / len(keywords)) * paper_question.score)
                    total_score += score
                    score_list.append(score)

            result = Results.objects.create(
                exam=exam,
                user=user,
                total_score=total_score
            )

            for question, solution, score in zip(question_list, solution_list, score_list):
                ResultQuestionSolution.objects.create(
                    results=result,
                    question=question,
                    solution=solution,
                    score=score
                )
                # 提交答题卡时 判断是否答错
                paper_profile = exam.paper
                paper_question = PaperQuestion.objects.filter(question=question, paper=paper_profile).first()
                if score != paper_question.score:
                    # 更新错题库
                    wrong_question = WrongQuestions.objects.filter(user=user, question=question).first()
                    if wrong_question:
                        wrong_question.mastery -= 20
                        wrong_question.wrong_number += 1
                        wrong_question.wrong_time = timezone.now()
                        wrong_question.save()

            return api_response(ResponseCode.SUCCESS, '答题成功！', {'id': result.id})
        except Exception as e:
            return api_response(ResponseCode.BAD_REQUEST, f'请求参数不合法{str(e)}')


class WrongQuestion(APIView):
    """
    错题集合
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    @swagger_auto_schema(
        operation_description="错题集合",
        manual_parameters=[
            openapi.Parameter('user_id', openapi.IN_QUERY, description="用户ID", type=openapi.TYPE_STRING),
        ],
        responses={
            200: openapi.Response('获取成功！', WrongQuestionSerializer(many=True)),
            400: openapi.Response('请求参数不合法'),
            404: openapi.Response('数据不存在'),
        },
        tags=["用户管理"]
    )
    def get(self, request):
        try:
            user_id = request.query_params.get('user_id')
            user = User.objects.filter(id=user_id).first()
            if not user:
                return api_response(ResponseCode.NOT_FOUND, '用户不存在')
            results = Results.objects.filter(user=user).order_by('-created_at')
            if not results:
                return api_response(ResponseCode.SUCCESS, '未找到用户有答卷')
            # 存储已处理的题目，保证每个题目只处理一次
            seen_questions = set()
            for result in results:
                paper = result.exam.paper
                # 获取答题卡的所有题目
                result_question_solutions = (ResultQuestionSolution.objects.filter(results=result)
                                             .order_by('question', '-created_at'))
                for result_question_solution in result_question_solutions:
                    question = result_question_solution.question
                    if not question:
                        return api_response(ResponseCode.NOT_FOUND, '题目不存在')
                    # 如果题目已经处理过，则跳过
                    if question.id in seen_questions:
                        continue
                    # 将题目标记为已处理
                    seen_questions.add(question.id)
                    # 获取试卷题目关联表
                    paper_question = PaperQuestion.objects.filter(paper=paper, question=question).first()
                    if not paper_question:
                        return api_response(ResponseCode.NOT_FOUND, '试卷题目不存在')
                    question_score = paper_question.score  # 题目分数
                    user_score = result_question_solution.score  # 用户得分

                    if user_score != question_score:
                        old_wrong_question = WrongQuestions.objects.filter(user=user, question=question).first()

                        if not old_wrong_question:
                            WrongQuestions.objects.create(
                                user=user,
                                question=question,
                                solution=result_question_solution.solution,
                                score=user_score,
                                wrong_time=result_question_solution.created_at,  # 错题时间为答题数据的创建时间
                                wrong_number=1,  # 初始错误次数为1
                                mastery=80,  # 初始掌握程度为80
                            )
            wrong_question_list = WrongQuestions.objects.filter(user=user)
            if wrong_question_list:
                serializer = WrongQuestionSerializer(wrong_question_list, many=True)

                return api_response(ResponseCode.SUCCESS, '获取成功', serializer.data)
        except Exception as e:
            return api_response(ResponseCode.BAD_REQUEST, f'内部错误{str(e)}')



class RepeatWrongQuestion(APIView):
    """
    重做错题
    """
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    @swagger_auto_schema(
        operation_description="重做错题",
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['user_id', 'questions_solutions'],
            properties={
                'user_id': openapi.Schema(type=openapi.TYPE_STRING, description='用户ID'),
                'questions_solutions': openapi.Schema(
                    type=openapi.TYPE_ARRAY,
                    items=openapi.Schema(
                        type=openapi.TYPE_OBJECT,
                        properties={
                            'question': openapi.Schema(type=openapi.TYPE_STRING, description='试题ID'),
                            'solution': openapi.Schema(type=openapi.TYPE_STRING, description='答题内容'),
                        },
                    ),
                    description='试题数组，每个试题包含试题ID和答题内容',
                )
            },
        ),
        responses={
            200: openapi.Response(
                '创建成功！',
                openapi.Schema(
                    type=openapi.TYPE_OBJECT,
                    properties={
                        'id': openapi.Schema(type=openapi.TYPE_STRING, description='答题卡id'),
                    }
                )
            ),
            400: openapi.Response('请求参数不合法'),
            409: openapi.Response('答题卡已存在'),
            404: openapi.Response('答题卡不存在'),
        },
        tags=["用户管理"]
    )
    def post(self, request):
        try:
            user_id = request.data.get('user_id')
            user = User.objects.filter(id=user_id).first()
            if not user:
                return api_response(ResponseCode.NOT_FOUND, '用户不存在')

            questions_solutions = request.data.get('questions_solutions', [])

            wrongs= WrongQuestions.objects.filter(user=user)

            # 判断是否已经掌握
            wrong_num = 0
            for wrong in wrongs:
                if wrong.mastery != 100:
                    wrong_num = 1
                    break
            if wrong_num == 0:
                return api_response(ResponseCode.NO_CONTENT, '错题已经全掌握了')

            for question_info in questions_solutions:
                question_id = question_info.get('question')
                solution_str = question_info.get('solution')

                question_profile = Questions.objects.filter(id=question_id).first()
                if not question_profile:
                    return api_response(ResponseCode.NOT_FOUND, '试题不存在')

                # 错题记录
                wrong_question = WrongQuestions.objects.filter(user=user, question=question_profile).first()

                # 题目答案
                answer = question_profile.answer
                wrong_question.solution = solution_str

                if answer == solution_str:
                    wrong_question.mastery += 20
                    wrong_question.save()

                    if wrong_question.mastery > 100:
                        wrong_question.mastery = 100
                        wrong_question.save()

                elif answer != solution_str:
                    wrong_question.mastery -= 20
                    wrong_question.wrong_number += 1
                    wrong_question.wrong_time = timezone.now()
                    wrong_question.save()

                    if wrong_question.mastery < 0:
                        wrong_question.mastery = 0
                        wrong_question.save()

            return api_response(ResponseCode.SUCCESS, '重做错题成功')
        except Exception as e:
            return api_response(ResponseCode.BAD_REQUEST, f'内部错误{str(e)}')

